package com.jt.www.aop;

import com.jt.www.annotations.DuplicateToken;
import com.jt.www.exception.ServiceException;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.UUID;

@Aspect
@Component
public class TokenCheckHandler {

    public static final Logger log = LoggerFactory.getLogger(TokenCheckHandler.class);

    /**
     * 获取注解中对方法的描述信息
     *
     * @param joinPoint 切点
     * @return 方法描述
     * @throws Exception
     */
    private static DuplicateToken getMethodDescription(JoinPoint joinPoint)
            throws Exception {
        String description = "";
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        DuplicateToken duplicateToken = method.getAnnotation(DuplicateToken.class);
        return duplicateToken;
    }

    /**
     * 获取请求的方法名全路径
     *
     * @param joinPoint
     * @return
     */
    private static String getFullMethodName(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();

        //请求的方法名全路径
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = signature.getName();

        return className + "." + methodName + "()";
    }

    /**
     * 定义日志切入点
     */
    @Pointcut("@annotation(com.jt.www.annotations.DuplicateToken)")
    public void logPointCut() {
    }

    /**
     * 前置通知 用于拦截service层记录用户的操作
     *
     * @param joinPoint 切点
     */
    @Before("logPointCut()")
    public void doBefore(JoinPoint joinPoint) {
        try {
            HttpServletRequest request = null;
            //token 的key
            String key = getFullMethodName(joinPoint);
            DuplicateToken duplicateToken = getMethodDescription(joinPoint);
            if (duplicateToken != null) {
                ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                request = attributes.getRequest();
                if (duplicateToken.save()) {
                    Object attribute = request.getSession().getAttribute(key);
                    if (attribute == null) {
                        //生成token并缓存
                        String uuid = UUID.randomUUID().toString();
                        request.getSession().setAttribute(key, uuid);
                        log.info("token-key=" + key);
                        log.info("token-value=" + uuid);
                    } else {
                        throw new ServiceException("请求已发送,请勿重复提交");
                    }
                }
            }
        } catch (Exception e) {
            //记录本地异常日志
            log.error("==前置通知异常==");
            log.error("异常信息:{}", e.getMessage());
        }
    }

    @AfterReturning("logPointCut()")
    public void doAfter(JoinPoint joinPoint) throws Exception {
        log.info("出方法：end");
        DuplicateToken duplicateToken = getMethodDescription(joinPoint);
        String key = getFullMethodName(joinPoint);
        if (duplicateToken != null) {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            if (duplicateToken.save()) {
                Object attribute = request.getSession().getAttribute(key);
                if (null != attribute) {
                    request.getSession(false).removeAttribute(key);
                }
            }
        }
    }

    /**
     * 异常
     *
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(pointcut = "logPointCut()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Throwable e) throws Exception {
        DuplicateToken duplicateToken = getMethodDescription(joinPoint);
        if (null != duplicateToken) {
            //处理重复提交本身之外的异常
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            boolean isSaveSession = duplicateToken.save();
            //获得方法名称
            if (isSaveSession) {
                String key = getFullMethodName(joinPoint);
                Object t = request.getSession().getAttribute(key);
                if (null != t) {
                    //方法执行完毕移除请求重复标记
                    request.getSession(false).removeAttribute(key);
                    log.info("异常情况--移除标记！");
                }
            }
        }
    }


}
